﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class BoyController : MonoBehaviour {

	bool m_Controllable = true;
	bool m_GameOver = false;

	public bool Controllable {
		get {
			return m_Controllable;
		}
	}

	public CharacterController m_CC {
		get;
		set;
	}
	public Animator m_Animator {
		get;
		set;
	}

	public float m_Movement {
		get;
		set;
	}

	bool m_GotMovement;

	[SerializeField] private Transform m_GroundCheck;
	[SerializeField] private LayerMask m_GroundLayerMasks;

	public Transform m_JumpFixPoint;
	public Transform m_JumpFixObstacleCheck;
	public Transform m_JumpFixOverPoint;
	public Transform m_JumpFixMaxY;
	public float m_JumpFixRadius;
	public float m_JF_ObstacleRadius;
	public LayerMask m_JumpFixLayer;
	Vector2 vertex = Vector2.zero;
	RaycastHit2D m_RaycastClimb;
	Transform m_ClimbObject;
	Vector3 m_LastClimbPos;

	Vector2 obstaclePos = Vector2.zero;

	[System.Serializable]
	private class BirdMoveEvent : UnityEvent<float, bool, bool> { }

	[Header("Events")]
	[Space]
	[SerializeField] private BirdMoveEvent birdEvent;
	[SerializeField] private BoolEvent birdCrouchEvent;
	[SerializeField] private BoolEvent ControllBirdEvent;

	BoyAudioPlayer BAP;


	private bool running;
	private bool jumping;
	private bool jumpFix;
	private bool crouching;
	private bool falling;

	private float pulling;
	private bool pushing;
	private bool climbing;

	public bool JumpFixing {
		get => jumpFix;
	}

	private void Awake() {
		m_CC = gameObject.GetComponent<CharacterController>();
		m_Animator = gameObject.GetComponent<Animator>();
		BAP = GetComponent<BoyAudioPlayer>();

		if (birdEvent == null)
			birdEvent = new BirdMoveEvent();

		if (birdCrouchEvent == null)
			birdCrouchEvent = new BoolEvent();

		if (ControllBirdEvent == null)
			ControllBirdEvent = new BoolEvent();

		EventManager.Add("gameover", GameOver);

	}
	private void Update() {

		if (InputReader.ChangeController) {
			ControllBirdEvent.Invoke(!(m_Controllable = !m_Controllable));
			m_Animator.SetFloat("Moving", 0);
		}

	}

	private void FixedUpdate() {

		if (m_GameOver)
			return;
		if (!m_Controllable)
			return;

		JumpFix();

	}

	private void OnDestroy() {
		EventManager.Reomve("gameover", GameOver);
	}


	/// <summary>
	///  跳跃攀爬修正
	/// </summary>
	void JumpFix() {

		//移动中的物体攀爬
		if (jumpFix) {
			Debug.Log((Vector2)(m_ClimbObject.position - m_LastClimbPos));
			if (vertex != Vector2.zero) {
				vertex += (Vector2)(m_ClimbObject.position - m_LastClimbPos);
				transform.position += m_ClimbObject.position - m_LastClimbPos;
				if (m_LastClimbPos != m_ClimbObject.position) {
					m_LastClimbPos = m_ClimbObject.position;
				}
			}
		}

		if (jumpFix || ((m_Animator.GetBool("Jumping")) && !m_CC.Grounded)) {

			m_RaycastClimb = Physics2D.Raycast(m_JumpFixMaxY.position, Vector3.right * transform.localScale.x, .5f, m_JumpFixLayer);
			//斜角度检测
			if (Physics2D.Raycast(m_JumpFixMaxY.position, Vector3.right * transform.localScale.x + Vector3.up, .8f, m_JumpFixLayer))
				return;

			if (ClimbFix(transform.localScale.x))
				return;

		}

		MoveForward();

	}
	/// <summary>
	///  人物移动 以及 鸟的相关反应
	/// </summary>
	public void MoveForward() {

		m_CC.Move(m_Movement, running, crouching, jumping, jumpFix, pulling, climbing);

		if(m_CC.Grounded && !climbing)
			BAP.PlayMoveSound(Mathf.Abs(running ? m_Movement * 2 : m_Movement));

		jumping = false;
		birdEvent.Invoke(m_Movement, crouching, running);
	}
	/// <summary>
	///  攀爬墙角修正
	/// </summary>
	/// <returns></returns>
	public bool ClimbFix(float face) {

		//顶侧障碍物检测
		RaycastHit2D[] obstacle = Physics2D.RaycastAll(m_JumpFixObstacleCheck.position, Vector3.up, 1f);
		if (ObstacleCheck(obstacle)) {
			Debug.Log("上方有障碍");
			return false;
		}
		//旁侧障碍物检测
		obstacle = Physics2D.RaycastAll(m_JumpFixObstacleCheck.position, Vector3.right * face, 1f);
		if (ObstacleCheck(obstacle)) {
			Debug.Log("前方有障碍");
			return false;
		}

		m_RaycastClimb = Physics2D.Raycast(m_JumpFixMaxY.position, Vector3.right * face, .5f, m_JumpFixLayer);
		//Debug.Log(m_RaycastClimb? m_RaycastClimb.transform.name : "检测不到攀爬物");
		if (m_RaycastClimb) {
			m_ClimbObject = m_RaycastClimb.transform;
			m_LastClimbPos = m_ClimbObject.position;
			//获取动画播放点
			if (vertex == Vector2.zero) {
				RaycastHit2D rh;
				float angle = face > 0 ? 0 : 180;
				for (; (angle < 90 && face > 0) || (angle > 90 && face < 0); angle += face) {
					rh = Physics2D.Raycast(m_JumpFixMaxY.position, new Vector2(Mathf.Cos(angle * Mathf.PI / 180), Mathf.Sin(angle * Mathf.PI / 180)), .8f, m_JumpFixLayer);
					if (!rh) {
						angle -= face;
						m_RaycastClimb = Physics2D.Raycast(m_JumpFixMaxY.position, new Vector2(Mathf.Cos(angle * Mathf.PI / 180), Mathf.Sin(angle * Mathf.PI / 180)), .8f, m_JumpFixLayer);
						vertex = m_RaycastClimb.point;
						m_CC.rb.transform.position += (Vector3)(vertex - (Vector2)m_JumpFixPoint.position + (Vector2)(m_ClimbObject.position - m_LastClimbPos));
						break;
					}
				}
				RaycastHit2D[] rh2 = Physics2D.RaycastAll(vertex, Vector2.up + Vector2.right * face, Mathf.Sqrt(2));
				if (ObstacleCheck(rh2))
					return false;

			}

			if (!jumpFix) {
				jumpFix = true;
				m_Animator.SetBool("JumpFix", true);
				m_Animator.SetBool("Jumping", false);
				m_CC.rb.bodyType = RigidbodyType2D.Static;
			}
			return true;
		}

		return false;
	}

	bool ObstacleCheck(RaycastHit2D[] rh2) {
		foreach (RaycastHit2D r in rh2) {
			if (!r.collider.Equals(m_RaycastClimb.collider) && r.collider.tag == TagAndLayersConfigs.Tag.Box) {
				obstaclePos = r.point;
				Debug.Log("攀爬物 : " + m_RaycastClimb.collider + "  遇到障碍 " + r.collider.name);
				OnJumpFixFailure();
				return true;
			}
		}
		return false;
	}

	public void ClimbOpenLadder() {
		for (float face = 0; face < 2; face++) {
			if (ClimbFix(transform.localScale.x))
				break;
			m_CC.Flip(-transform.localScale.x);
		}
		m_Animator.SetBool("Climbing", false);
	}

	/// <summary>
	///  障碍物检测 则 取消攀爬修正
	/// </summary>
	public void OnJumpFixFailure() {
		jumpFix = false;
		m_Animator.SetBool("JumpFix", false);
		vertex = Vector2.zero;
		//m_CC.rb.bodyType = RigidbodyType2D.Dynamic;
		m_RaycastClimb = Physics2D.Raycast(new Vector2(m_JumpFixMaxY.position.x, m_JumpFixMaxY.position.y), Vector3.right, .5f, m_JumpFixLayer);
	}
	/// <summary>
	///  攀爬修正结束
	/// </summary>
	public void OnJumpFixed() {

		//m_CC.rb.transform.position += (Vector3)(vertex - (Vector2)m_JumpFixOverPoint.position) ;
		transform.position += (Vector3)(vertex - (Vector2)m_JumpFixOverPoint.position);
		jumpFix = false;
		m_Animator.SetBool("JumpFix", false);
		vertex = Vector2.zero;
		m_CC.rb.bodyType = RigidbodyType2D.Dynamic;
		m_RaycastClimb = Physics2D.Raycast(new Vector2(m_JumpFixMaxY.position.x, m_JumpFixMaxY.position.y), Vector3.right, .5f, m_JumpFixLayer);
	}
	/// <summary>
	///  获取人物移动方向
	///  包含跳跃、匍行
	/// </summary>
	/// <param name="pull"></param>
	public void Movement(float pull, bool climb) {

		if (m_GameOver)
			return;
		if (!m_Controllable)
			return;

		if (InputReader.Crouch && !climb && m_CC.Grounded && !m_CC.GroundIsPlatform) {
			crouching = true;
		} else {
			crouching = false;
		}
		if (InputReader.Jump) {
			if (InputReader.Down) {
				Collider2D[] c2d = Physics2D.OverlapCircleAll (m_GroundCheck.position, .2f, m_GroundLayerMasks);
				foreach (var c in c2d) {
					if (c != null && c.tag == TagAndLayersConfigs.Tag.Platform) {
						crouching = false;
						jumping = false;
						StartCoroutine(BoyIgnoreCollision());
					}
				}
			} else {
				jumping = true;
			}
		}


		if (!m_GotMovement)
			m_Movement = InputReader.HorizontalRaw;
		else
			m_GotMovement = false;

		pulling = pull;
		climbing = climb;

		if (pulling <= 0 && !pushing) {
			running = InputReader.Run;
		}

		m_Animator.SetFloat("Moving", Mathf.Abs(running ? m_Movement * 2 : m_Movement));

	}

	IEnumerator BoyIgnoreCollision() {

		gameObject.layer = TagAndLayersConfigs.LayerMask.LayerName.Player;
		yield return new WaitForSeconds(.2f);
		//RaycastHit2D r = Physics2D.Raycast(m_GroundCheck.position, Vector2.down, 1)
		gameObject.layer = 17;

	}

	public void OnJumping(bool jump) {
		m_Animator.SetBool("Jumping", jump);
		jumping = jump;
	}
	public void OnFalling(bool fall) {
		m_Animator.SetBool("Falling", fall);
	}
	public void OnCrouching(bool crouch) {
		m_Animator.SetBool("Crouching", crouch);

		birdCrouchEvent.Invoke(crouch);
	}
	public void BridLostController() {
		m_Controllable = true;
	}

	public void GameOver(params object[] o){
		ControllBirdEvent.Invoke(false);
		m_Controllable = false;
		m_CC.rb.velocity = Vector2.zero;
	}

	public void Jump(float movement) {
		m_CC.rb.bodyType = RigidbodyType2D.Dynamic;
		m_GotMovement = true;
		m_Movement = movement;
		jumping = true;
	}
	private void OnTriggerStay2D(Collider2D collision) {
		if (m_Controllable && InputReader.InterActive && collision.CompareTag(TagAndLayersConfigs.Tag.IntertactiveItems)) {
			collision.GetComponent<InteractiveItems>().Interactive_Interface();
		}
	}
	bool ignoreGravity;
	private void OnCollisionStay2D(Collision2D collision) {

		if (collision.gameObject.tag == TagAndLayersConfigs.Tag.Platform && m_CC.Grounded && !jumping) {
			m_CC.rb.AddForce(-m_CC.rb.gravityScale * m_CC.rb.mass * Vector2.up * Physics2D.gravity);
		}

	}

	private void OnCollisionExit2D(Collision2D collision) {
		if (collision.gameObject.tag == TagAndLayersConfigs.Tag.Platform && m_CC.Grounded && !jumping) {
			m_CC.rb.AddForce(m_CC.rb.gravityScale * m_CC.rb.mass * Vector2.up * Physics2D.gravity);
		}
	}

	private void OnDrawGizmos() {

		Gizmos.color = Color.green;
		Gizmos.DrawLine(m_JumpFixMaxY.position, m_JumpFixMaxY.position + transform.localScale.x * Vector3.right / 2);
		Gizmos.DrawLine(m_JumpFixMaxY.position, m_JumpFixMaxY.position + transform.localScale.x * Vector3.right / 2 + Vector3.up / 2);

		Gizmos.color = Color.red;
		Gizmos.DrawLine(m_JumpFixObstacleCheck.position, m_JumpFixObstacleCheck.position + transform.localScale.x * Vector3.right);

		Gizmos.DrawLine(m_JumpFixObstacleCheck.position, m_JumpFixObstacleCheck.position + Vector3.up);

		Gizmos.DrawLine(vertex, vertex + Vector2.up + Vector2.right);

		//if (obstaclePos != Vector2.zero) {
		//	Gizmos.DrawCube(obstaclePos, Vector2.one);
		//}
	}

}
